﻿using Faml.Frontend;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace Faml.Optimization
{
    public interface IVisitor
    {
        FamlExpression Visit(FamlExpression node);
    }

    public class VisitOptimizer
    {
        public static FamlExpression Optimize(FamlExpression exp, IVisitor visitor)
        {
            int i = 0;
            switch (exp.NodeType)
            {
                case FamlExpressionType.Constant:
                    return exp;

                case FamlExpressionType.UnaryExpression:

                    FamlUnaryExpression ue = (FamlUnaryExpression)exp;

                    FamlExpression operand = Optimize(ue.Target, visitor);
                    ue = new FamlUnaryExpression(ue.Operator, operand);
                    return visitor.Visit(ue);

                case FamlExpressionType.BinaryExpression:

                    FamlBinaryExpression be = (FamlBinaryExpression)exp;

                    FamlExpression operandLeft = Optimize(be.Left, visitor);
                    FamlExpression operandRight = Optimize(be.Right, visitor);

                    be = new FamlBinaryExpression(operandLeft, operandRight, be.Operator);
                    return visitor.Visit(be);
                
                case FamlExpressionType.ConditionalExpression:
                    FamlConditionalExpression fce = (FamlConditionalExpression)exp;
                    return visitor.Visit(
                        new FamlConditionalExpression(
                            Optimize(fce.Condition, visitor),
                            Optimize(fce.TrueBranch, visitor),
                            Optimize(fce.FalseBranch, visitor)));

                case FamlExpressionType.NewArray:

                    FamlInlineArrayExpression nae = (FamlInlineArrayExpression)exp;

                    if (nae.Items.Count == 0)
                        return visitor.Visit(nae);

                    FamlExpression[] newitems = new FamlExpression[nae.Items.Count];
                    i = 0;
                    foreach (FamlExpression arg in nae.Items)
                    {
                        newitems[i++] = Optimize(arg, visitor);
                    }

                    nae = new FamlInlineArrayExpression(newitems);

                    return visitor.Visit(nae);


                

                case FamlExpressionType.Lambda:

                    FamlLambdaExpression lexp = (FamlLambdaExpression)exp;
                    IList<FamlLambdaParameter> newparams = lexp.Parameters;

                    FamlExpression body = Optimize(((FamlUnaryExpression)lexp.Body).Target, visitor);

                    lexp = new FamlLambdaExpression(body, newparams);
                    return visitor.Visit(lexp);


                case FamlExpressionType.FunctionCall:

                    FamlFunctionCallExpression mce = (FamlFunctionCallExpression)exp;

                    FamlExpression[] newargs = new FamlExpression[mce.Arguments.Count];
                    i = 0;
                    foreach (FamlExpression arg in mce.Arguments)
                    {
                        newargs[i++] = Optimize(arg, visitor);
                    }

                    mce = new FamlFunctionCallExpression(mce.Operation, newargs);
                    return visitor.Visit(mce);

                case FamlExpressionType.Dictionary:

                    FamlDictionaryExpression src = (FamlDictionaryExpression)exp;
                    OrderedDictionary optDict = new OrderedDictionary();
                    foreach(string field in src.GetKeys())
                        optDict.Add(field, Optimize(src.GetField(field), visitor));
                    return visitor.Visit(new FamlDictionaryExpression(optDict)); 
                                  
                default:
                    return visitor.Visit(exp);
            }
        }
    }
}
